﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Net;

namespace Per_pixel_OCR
{
    public static class FileOperations
    {
        // File properties:
        // int: numdigits
        // int: width
        // int: height
        // byte: digit (0,1, ... ,9)
        // byte: width x height times pixel ID's

        public static Digit[] ReadNewDB(string path)
        {
            Digit[] rVal;

            using (BinaryReader b = new BinaryReader(File.Open(path, FileMode.Open)))
            {
                int length = b.ReadInt32();
                int width = b.ReadInt32();
                int height = b.ReadInt32();

                rVal = new Digit[length];

                for (int k = 0; k < rVal.Length; k++)
                {
                    int digitID = b.ReadInt32();
                    byte digitValue = b.ReadByte();
                    DigitType digitType = (DigitType)b.ReadByte();

                    byte[,] original = new byte[height, width];
                    byte[,] tags = new byte[height, width];

                    for (int i = 0; i < height; i++)
                        for (int j = 0; j < width; j++)
                            original[i, j] = b.ReadByte();

                    for (int i = 0; i < height; i++)
                        for (int j = 0; j < width; j++)
                            tags[i, j] = b.ReadByte();

                    rVal[k] = new Digit(digitID, digitValue, original, tags);
                }
            }

            return rVal;
        }        

        public static void WriteNewDatabase(Digit[] digits, string path)
        {
            using (BinaryWriter b = new BinaryWriter(File.Open(path, FileMode.Create)))
            {
                int numItems = digits.Length;
                int numRows = 28;
                int numCols = 28;

                b.Write(numItems);
                b.Write(numRows);
                b.Write(numCols);

                for (int i = 0; i < digits.Length; i++)
                {
                    b.Write(digits[i].DigitID);
                    b.Write((byte)digits[i].DigitValue);
                    b.Write((byte)digits[i].DigitType);

                    for (int j = 0; j < digits[i].OriginalGrayscaleMatrix.GetLength(0); j++)
                        for (int k = 0; k < digits[i].OriginalGrayscaleMatrix.GetLength(1); k++)
                            b.Write((byte)digits[i].OriginalGrayscaleMatrix[j, k]);

                    for (int j = 0; j < digits[i].TagMatrix.GetLength(0); j++)
                    {
                        for (int k = 0; k < digits[i].TagMatrix.GetLength(1); k++)
                        {
                            b.Write(digits[i].TagMatrix[j, k]);
                        }
                    }
                }
            }
        }

        public static Digit[] Only(string path, int[] selectedDigits, int numItems = -1)
        {
            List<Digit> rVal = new List<Digit>();

            using (BinaryReader b = new BinaryReader(File.Open(path, FileMode.Open)))
            {
                int length = b.ReadInt32();
                int width = b.ReadInt32();
                int height = b.ReadInt32();

                int counter = 0;
                for (int k = 0; k < length; k++)
			    {			    
                    int digitID = b.ReadInt32();
                    byte digitValue = b.ReadByte();

                    if(selectedDigits.Contains(digitValue))
                    {
                        DigitType digitType = (DigitType)b.ReadByte();

                        byte[,] original = new byte[height, width];
                        byte[,] tags = new byte[height, width];

                        for (int i = 0; i < height; i++)
                            for (int j = 0; j < width; j++)
                                original[i, j] = b.ReadByte();

                        for (int i = 0; i < height; i++)
                            for (int j = 0; j < width; j++)
                                tags[i, j] = b.ReadByte();

                        rVal.Add(new Digit(digitID, digitValue, original, tags));
                        counter++;
                    }
                    else
                    {
                        b.BaseStream.Seek(1 + width * height * 2, SeekOrigin.Current);
                    }

                    if (numItems > 0 && counter == numItems)
                    {
                        break;
                    }
                }
            }

            return rVal.ToArray();


        }

        /// <summary>
        ///   Returns gray-scale digit images from a given MNIST data source.
        /// </summary>
        /// 
        /// <param name="path">Path of MNIST data source.</param>
        /// <returns>The grays-scale images in the given MNIST data source path.</returns>
        /// 
        public static byte[][,] ReadImages(string path, int skip = 0, int count = 0)
        {
            byte[][,] rVal;

            using (BinaryReader b = new BinaryReader(File.Open(path, FileMode.Open)))
            {
                int length = (int)b.BaseStream.Length;

                int magicNumber = IPAddress.NetworkToHostOrder(b.ReadInt32());
                int numItems = IPAddress.NetworkToHostOrder(b.ReadInt32());
                int numRows = IPAddress.NetworkToHostOrder(b.ReadInt32());
                int numCols = IPAddress.NetworkToHostOrder(b.ReadInt32());

                rVal = count < 1 ? new byte[numItems][,] : new byte[count][,];

                b.BaseStream.Seek(skip * numRows * numCols, SeekOrigin.Current);

                for (int k = 0; k < rVal.Length; k++)
                {
                    byte[,] tempArray = new byte[numRows, numCols];

                    for (int i = 0; i < numRows; i++)
                        for (int j = 0; j < numCols; j++)
                            tempArray[i, j] = b.ReadByte();

                    rVal[k] = tempArray;
                }
            }

            return rVal;
        }

        /// <summary>
        ///   Returns labels of gray-scale digit images from a given MNIST data source.
        /// </summary>
        /// 
        /// <param name="path">Path of MNIST data source.</param>
        /// <returns>The labels of images from the given MNIST data source path. .</returns>
        /// 
        public static int[] ReadLabels(string path, int skip = 0, int count = 0)
        {
            int[] rVal;

            using (BinaryReader b = new BinaryReader(File.Open(path, FileMode.Open)))
            {
                int magicNumber = IPAddress.NetworkToHostOrder(b.ReadInt32());
                int numItems = IPAddress.NetworkToHostOrder(b.ReadInt32());

                rVal = count < 1 ? new int[numItems] : new int[count];

                b.BaseStream.Seek(skip, SeekOrigin.Current);

                for (int i = 0; i < rVal.Length; i++)
                {
                    rVal[i] = b.ReadByte();
                }
            }

            return rVal;
        }

        /// <summary>
        ///   Returns binary digit images from a given MNIST data source.
        /// </summary>
        /// 
        /// <param name="path">Path of MNIST data source.</param>
        /// <returns>The binary images in the given MNIST data source path.</returns>
        /// 
        public static byte[][,] ReadImagesBinary(string path, int skip = 0, int count = 0)
        {
            byte[][,] rVal = ReadImages(path, skip, count);

            for (int i = 0; i < rVal.Length; i++)
                BinarizeImage(rVal[i]);

            return rVal;
        }

        private static void BinarizeImage(byte[,] inputImage)
        {
            for (int i = 0; i < inputImage.GetLength(0); i++)
                for (int j = 0; j < inputImage.GetLength(1); j++)
                    inputImage[i, j] = inputImage[i, j] > 128 ? (byte)1 : (byte)0;
        }

        public static void CreateDB()
        {
            var inputs = FileOperations.ReadImagesBinary("C:\\trainInputs.data");
            var outputs = FileOperations.ReadLabels("C:\\trainOutputs.data");

            using (BinaryWriter b = new BinaryWriter(File.Open("C:\\train.data", FileMode.Create)))
            {
                int numItems = inputs.Length;
                int numRows = 28;
                int numCols = 28;

                b.Write(numItems);
                b.Write(numRows);
                b.Write(numCols);

                for (int i = 0; i < inputs.Length; i++)
                {
                    b.Write((byte)outputs[i]);
                    for (int j = 0; j < inputs[i].GetLength(0); j++)
                        for (int k = 0; k < inputs[i].GetLength(1); k++)
                            b.Write((byte)inputs[i][j, k]);
                }
            }
        }         

        public static string GetPath()
        {
            int counter = -1;
            string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);

            string rVal = "";
            do
            {
                counter++;
                rVal = desktopPath + "\\logs" + counter.ToString("D3") + ".txt";
            }
            while (File.Exists(rVal));

            return rVal;
        }
    }
}
